/*
 *  Yatun  - Yet another tunneling application
 *
 *  Michal Demin - 2007
 *
 *  IO_TCP.cpp 
 *  TCP komunikacny modul
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/types.h>

#include <fcntl.h>


#include "IO_TCP.h"

IO_TCP::IO_TCP (struct sockaddr_in *tmpaddr)
{
  const char *name_loc = "IO_TCP";
  // skopirujem meno "modulu"
  memset (&Name, 0, NAME_LENGHT);
  memcpy (&Name, name_loc, 6);
   // skopirujem lokalnu adresu 
  memcpy (&localaddr, tmpaddr, sizeof (struct sockaddr_in));
  Flag = PLUG_FLAG_IO;
  listen_on = 0;
}

IO_TCP::IO_TCP ()
{
  const char *name_loc = "IO_TCP";

  memset (&Name, 0, NAME_LENGHT);
  memcpy (&Name, name_loc, 6);
 // naplnim lokalnu adresu a port
  localaddr.sin_family = AF_INET;
  localaddr.sin_port = htons (DEFAULT_PORT);
  localaddr.sin_addr.s_addr = INADDR_ANY;

  Flag = PLUG_FLAG_IO;
  listen_on = 0;
}

IO_TCP::~IO_TCP ()
{ 
// zahodim nepotrebne sockety
  if (fd == socket_fd) {
    close (fd);
  } else {
    close (fd);
    close (socket_fd);
  }
}

void
IO_TCP::SetAddress (struct sockaddr_in *tmpaddr)
{
//    memset(&localaddr, 0, sizeof(localaddr));
  memcpy (&remoteaddr, tmpaddr, sizeof (struct sockaddr_in));
}

int
IO_TCP::Open ()
{
  int reuse = 1;
  int ret;

  // otvorim socket 
  socket_fd = socket (AF_INET, SOCK_STREAM, 0);
  ret = setsockopt (socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof (reuse));
  if (ret) {
    printf ("unable to set address reuse \n");
    return -1;
  }

  return 0;
}

int
IO_TCP::Listen ()
{
// pri server mode sa musim bindnut na port a potom neskor cakat na prichadzajuce spojenia
  int ret;
  listen_on = 1;
  ret = bind (socket_fd, (struct sockaddr *) &localaddr, sizeof (struct sockaddr));
  if (ret) {
    printf ("unable to bind socket  \n");
    return -1;
  }

  return 0;
}

int
IO_TCP::Connect ()
{
  int ret;
 
  if (listen_on == 1) {
    ret = bind (socket_fd, (struct sockaddr *) &localaddr, sizeof (struct sockaddr));
    if (ret) {
      printf ("unable to bind socket  \n");
      return -1;
    }
    // cakame na spojenie z vonka 
    ret = listen (socket_fd, 2);
    if (ret) {
      printf ("unable to listen to socket  \n");
      return -1;
    }

    printf ("Waiting for incomming connection \n");
    fd = accept (socket_fd, 0, 0);
    printf ("There ... connected, new fd = %d\n", fd);
  } else {
    fd = socket_fd;
    /// tu sa hrame na klienta, tak sa pripojim na vzdialeny server
    ret = connect (socket_fd, (struct sockaddr *) &remoteaddr, sizeof (struct sockaddr));
    if (ret) {
      printf ("unable to connect to remote host !\n");
      return -1;
    }

  }
  return 0;
}

int
IO_TCP::Close ()
{
  close (fd);
  return 0;
}

int
IO_TCP::Recieve (Packet * Pack)
{
  int ret;

  ret = read (fd, Pack, sizeof (Packet));
  YATUN_PACKET_2HOST(Pack);
//  Pack->Lenght = ntohs(Pack->Lenght); 
  
  if (ret <= 0)
    return -1;

  return ret;
}

int
IO_TCP::Send (Packet * Pack)
{
  int ret;
  int len = Pack->Lenght + YATUN_OVERHEAD;

//  Pack->Lenght = htons(Pack->Lenght); 
//    fprintf(stderr, "parametra vrajtu ... len = %d \n",len);
  YATUN_PACKET_2NET(Pack);
  ret = write (fd, Pack, len);
  YATUN_PACKET_2HOST(Pack); // koli MALYM a velkym endianom
//  Pack->Lenght = ntohs(Pack->Lenght);  // koli kompatibilite little a big endian
//    Pack->Lenght = len;

  return ret;
}


int
IO_TCP::DataReady ()
{
  // skontrolujeme ci nas citanie nebude blokovat 
  struct timeval timeout;
  fd_set rfds;
  int ret;

  timeout.tv_sec = 0;
  timeout.tv_usec = 20;

  FD_ZERO (&rfds);
  FD_SET (fd, &rfds);

  ret = select (fd + 1, &rfds, NULL, NULL, &timeout);

  if (ret == -1) {
    return 0;
  } else if (ret) {
    if (FD_ISSET (fd, &rfds)) {
      return 1;
    }
  }

  return 0;
}

int
IO_TCP::GetFD ()
{
  return fd;
}
